package state.socket;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import state.exeption.DataNotFoundException;
import state.exeption.WrongDataTypeException;

public class MapDataPackage {
  public static final int BYTE_ARRAY_TYPE = 0;
  public static final int INT_TYPE = 1;
  public static final int DOUBLE_TYPE = 2;
  public static final int LONG_TYPE = 3;
  public static final int STRING_TYPE = 4;
  
  private DataPackage innerDataPackage;
  
  private Hashtable dataByName = new Hashtable();
  private Hashtable typeByName = new Hashtable();
  
  private static final String[] DATA_TYPE_NAME = new String[] {"Byte Array", "integer", "double", "long", "String"};
  
  private int size = 0;
  
  public MapDataPackage(int header) {
    innerDataPackage = new DataPackage(header);
  }
  
  public MapDataPackage(int header, int size) {
    innerDataPackage = new DataPackage(header, size);
  }
  
  public static MapDataPackage getMapPackage(DataPackage dataPackage) {
    return new MapDataPackage(dataPackage);
  }
  
  private MapDataPackage(DataPackage dataPackage) {
    innerDataPackage = dataPackage;
    try {
      while(true) {
        String name = innerDataPackage.nextString();
        int type = innerDataPackage.nextInt();
        typeByName.put(name, new Integer(type));
        
        switch (type) {
        case BYTE_ARRAY_TYPE:
          dataByName.put(name, innerDataPackage.nextByteArray());
          break;
        case INT_TYPE:
          dataByName.put(name, new Integer(innerDataPackage.nextInt()));
          break;
        case DOUBLE_TYPE:
          dataByName.put(name, new Double(innerDataPackage.nextDouble()));
          break;
        case LONG_TYPE:
          dataByName.put(name, new Long(innerDataPackage.nextLong()));
          break;
        case STRING_TYPE:
          dataByName.put(name, innerDataPackage.nextString());
          break;
        default:
          throw new WrongDataTypeException("Can not relize the data type: " + type);
        }
        size++;
      }
    } catch (ArrayIndexOutOfBoundsException ex) {
    }
  }
  
  public Vector getNames() {
    Enumeration enum1 = typeByName.keys();
    Vector names = new Vector();
    for (; enum1.hasMoreElements(); ) {
      names.addElement(enum1.nextElement());
    }
    return names;
  }
  
  public int getHeader() {
    return innerDataPackage.getHeader();
  }
  
  public void setHeader(int header) {
    innerDataPackage.setHeader(header);
  }
  
  public int getDataLength() {
    return innerDataPackage.getDataLength();
  }
  
  public DataPackage getDataPackage() {
    return innerDataPackage;
  }
  
  public int getSize() {
    return size;
  }

  public void putByteArray(String name, byte[] bytes) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(BYTE_ARRAY_TYPE);
    innerDataPackage.putByteArray(bytes);
    size++;
  }
  
  public void putInt(String name, int value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(INT_TYPE);
    innerDataPackage.putInt(value);
    size++;
  }
  
  public void putDouble(String name, double value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(DOUBLE_TYPE);
    innerDataPackage.putDouble(value);
    size++;
  }
  
  public void putLong(String name, long value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(LONG_TYPE);
    innerDataPackage.putLong(value);
    size++;
  }
  
  public void putString(String name, String value) {
    innerDataPackage.putString(name);
    innerDataPackage.putInt(STRING_TYPE);
    innerDataPackage.putString(value);
    size++;
  }
  
  public byte[] getByteArray(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    
    if (type.intValue() != BYTE_ARRAY_TYPE) {
      throw new WrongDataTypeException("Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Byte Array");
    }
    return (byte[]) dataByName.get(name);
  }
  
  public int getInt(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    
    if (type.intValue() != INT_TYPE) {
      throw new WrongDataTypeException("Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Integer");
    }
    return ((Integer) dataByName.get(name)).intValue();
  }
  
  public double getDouble(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    
    if (type.intValue() != DOUBLE_TYPE) {
      throw new WrongDataTypeException("Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Double");
    }
    return ((Double) dataByName.get(name)).doubleValue();
  }
  
  public long getLong(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    
    if (type.intValue() != LONG_TYPE) {
      throw new WrongDataTypeException("Data: " + name + " is type " + DATA_TYPE_NAME[type.intValue()] + ", not Long");
    }
    return ((Long) dataByName.get(name)).longValue();
  }
  
  public String getString(String name) {
    Object data = dataByName.get(name);
    if (data == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    return String.valueOf(data);
  }
  
  public byte[] popByteArray(String name) {
    byte[] value = getByteArray(name);
    removeData(name);
    return value;
  }
  
  public int popInt(String name) {
    int value = getInt(name);
    removeData(name);
    return value;
  }
  
  public double popDouble(String name) {
    double value = getDouble(name);
    removeData(name);
    return value;
  }
  
  public long popLong(String name) {
    long value = getLong(name);
    removeData(name);
    return value;
  }
  
  public String popString(String name) {
    String value = getString(name);
    removeData(name);
    return value;
  }
  
  public int getTypeOf(String name) {
    Integer type = (Integer) typeByName.get(name);
    if (type == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    return type.intValue();
  }
  
  public void removeData(String name) {
    typeByName.remove(name);
    dataByName.remove(name);
  }
  
  public Object get(String name) {
    Object data = dataByName.get(name);
    if (data == null) {
      throw new DataNotFoundException("MapDataPackage is not contain data name: " + name);
    }
    return data;
  }
  
  public MapDataPackage clone() {
    return new MapDataPackage(innerDataPackage.clone());
  }
  
  public void detroy() {
    innerDataPackage.detroy();
  }
}
